package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.config.AliIoTConfigProperties;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.service.DeviceService;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;


import com.github.pagehelper.Page;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class DeviceServiceImpl implements DeviceService {
    @Autowired
    private Client client;

    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private Properties pageHelperProperties;

    @Override
    public void getDeviceInfo() {
        QueryProductListRequest request = new QueryProductListRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        request.setCurrentPage(1);
        request.setPageSize(200);
        QueryProductListResponse response = null;
        try {
            response = client.queryProductList(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (response.getBody().success) {
            List<QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo> productInfo = response.getBody().getData().getList().productInfo;
            String jsonStr = JSONUtil.toJsonStr(productInfo);

            redisTemplate.opsForValue().set(CacheConstants.DEVICE_LIST,jsonStr);
        } else {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
    }

    @Override
    public List<ProductVo> getAllProduct() {
        String deviceList = redisTemplate.opsForValue().get(CacheConstants.DEVICE_LIST);
        List<ProductVo> productVos = JSONUtil.toList(deviceList, ProductVo.class);
        return productVos;


    }

    @Override
    public void registerDevice(DeviceDto deviceDto) {
        // 先判断是否存在该设备
        int count = deviceMapper.registerDevice(deviceDto.getDeviceName());
        if (count > 0){
            // 设备已存在
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }
        // 向物联网平台注册
        RegisterDeviceRequest request = deviceDto.getRegisterDeviceRequest();
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        request.setProductKey(deviceDto.getProductKey());
        RegisterDeviceResponse response = null;
        try {
            response = client.registerDevice(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (!response.getBody().success){
            // 注册失败
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        // 保存到数据库
        // 封装数据
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        device.setHaveEntranceGuard(0);
        device.setIotId(response.getBody().getData().getIotId());
        // 类型判断
        if (deviceDto.getLocationType() == 0){
            // 随身设备 -- 绑定老人
            device.setPhysicalLocationType(-1);
        }
        // 根据productKey查询产品名称
        QueryProductResponse resp = null;
        try {
            resp= client.queryProduct(new QueryProductRequest()
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                    .setProductKey(device.getProductKey())
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (!resp.getBody().getSuccess()){
            // 查询productName失败
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        device.setProductName(resp.getBody().getData().getProductName());
        // 保存到数据库
        DeleteDeviceResponse deleteResponse = null;
        try {
            deviceMapper.insert(device);
        }catch (Exception e){
            // 保存失败,需要删除平台的设备信息
            try {
                deleteResponse = client.deleteDevice(new DeleteDeviceRequest()
                        .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                        .setIotId(device.getIotId())
                        .setDeviceName(device.getDeviceName())
                        .setProductKey(device.getProductKey())
                );
            } catch (Exception ex) {
                throw new RuntimeException(ex);
            }
            if (!deleteResponse.getBody().getSuccess()){
                // 删除失败
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
        }
    }

    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(),devicePageQueryDto.getPageSize());
        Page<DeviceVo> page = deviceMapper.pageQueryDevice(devicePageQueryDto.getDeviceName(),devicePageQueryDto.getProductKey(),devicePageQueryDto.getLocationType());
        return PageResponse.of(page, DeviceVo.class);
    }

    @Override
    public DeviceVo queryDeviceDetail(DeviceDto deviceDto) {
        //根据id查询数据库的设备
        DeviceVo deviceVo = deviceMapper.queryDeviceDetail(deviceDto.getIotId());
        //根据iotId查询物联网平台的设备详情
        QueryDeviceDetailResponse response = null;

        try {
            response = client.queryDeviceDetail(new QueryDeviceDetailRequest()
                    .setIotId(deviceDto.getIotId())
                    .setProductKey(deviceDto.getProductKey())
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId()));
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = response.getBody().getData();
        BeanUtil.copyProperties(data,deviceVo);
        return deviceVo;
    }

    @Override
    public HashMap<String,QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyDataList> queryDevicePropertyStatus(DeviceDto deviceDto) {
        QueryDevicePropertyStatusResponse response = null;
        try {
            response = client.queryDevicePropertyStatus(new QueryDevicePropertyStatusRequest()
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                    .setProductKey(deviceDto.getProductKey())
                    .setIotId(deviceDto.getIotId()).setDeviceName(deviceDto.getDeviceName()));
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyDataList list = response.getBody().getData().getList();
        HashMap<String, QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyDataList> map = new HashMap<>();
        map.put("list",list);
        return map;
    }

    @Override
    public Map<String, String> queryThingModelPublished(DeviceDto deviceDto) {
        QueryThingModelPublishedResponse response = null;
        try {
            response = client.queryThingModelPublished(new QueryThingModelPublishedRequest()
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                    .setProductKey(deviceDto.getProductKey()));
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if (!response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        String thingModelJson = response.getBody().getData().getThingModelJson();
        Map<String, String> map = new HashMap<>();
        map.put("thingModelJson",thingModelJson);
        return map;
    }

    @Override
    public void updateDevice(DeviceDto deviceDto) {
        List<BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo> list = new ArrayList<>();
        list.add(new BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo()
                .setDeviceName(deviceDto.getDeviceName())
                .setNickname(deviceDto.getDeviceDescription())
                .setIotId(deviceDto.getIotId())
                .setProductKey(deviceDto.getProductKey()));
        BatchUpdateDeviceNicknameResponse response = null;
        try {
            response = client.batchUpdateDeviceNickname(new BatchUpdateDeviceNicknameRequest()
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                    .setDeviceNicknameInfo(list));
        } catch (Exception e) {
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }
        if (!response.getBody().success){
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        deviceMapper.updateById(device);
    }

    @Override
    public void deleteDevice(DeviceDto deviceDto) {
            DeleteDeviceRequest deleteRequest = new DeleteDeviceRequest()
                    .setIotInstanceId(aliIoTConfigProperties.getIotInstanceId())
                    .setProductKey(deviceDto.getProductKey())
                    .setDeviceName(deviceDto.getDeviceName())
                    .setIotId(deviceDto.getIotId());
        DeleteDeviceResponse deleteResponse = null;
        try {
                deleteResponse = client.deleteDevice(deleteRequest);

            } catch (Exception e) {
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
        if (!deleteResponse.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }
            deviceMapper.deleteById(deviceDto.getIotId());
    }

    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {
        LocalDateTime startData = LocalDateTimeUtil.of(startTime);
        LocalDateTime endData = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> list = deviceMapper.queryDeviceDataListByDay(iotId,functionId,startData,endData);
        Map<String, Double> collect = list.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));
        List<DeviceDataGraphVo> deviceDataGraphVos = DeviceDataGraphVo.dayInstance(startData);
        deviceDataGraphVos.forEach(item -> {
            Double dataValue = collect.get(item.getDateTime());
            if (dataValue != null){
                item.setDataValue(dataValue);
            }else {
                item.setDataValue(0.0);
            }

        });
        return deviceDataGraphVos;
    }

    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByweek(String iotId, String functionId, Long startTime, Long endTime) {
        LocalDateTime startData = LocalDateTimeUtil.of(startTime);
        LocalDateTime endData = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> list = deviceMapper.queryDeviceDataListByweek(iotId,functionId,startData,endData);
        Map<String, Double> collect = list.stream().collect(Collectors.toMap(DeviceDataGraphVo::getDateTime, DeviceDataGraphVo::getDataValue));
        List<DeviceDataGraphVo> deviceDataGraphVos = DeviceDataGraphVo.weekInstance(startData);
        deviceDataGraphVos.forEach(item -> {
            Double dataValue = collect.get(item.getDateTime());
            if (dataValue != null){
                item.setDataValue(dataValue);
            }else {
                item.setDataValue(0.0);
            }
        });
        return deviceDataGraphVos;
    }

}

